/*
 * dump.c
 * Jonathan Marokhovsky
 * WPI 2011
 * Possible arguments:
 *	-h
 *
 */

#include <stdio.h>
#include <stdlib.h> // for getenv()
#include <sys/stat.h> // for stat()
#include <sys/types.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>

#define MAX_SIZE 257

void usage(void);
int removeFile(char *file);
int emptyTrash(void);
int dirOp(char *dir);
char *getenv(const char *name);
void newStat(const char *file, struct stat *fileBuffer);
int dirDelete(char *dir, struct stat fileBuffer);
int stringLength(char *string);

char *trash;
int globalDebug;

int main(int argc, char *argv[])
{
	char theTrash[MAX_SIZE];
	int errflag, c;
	globalDebug = 1;
	
	trash = theTrash;
	trash = getenv("TRASH");
	if (!trash) // if the trash variable hasn't been defined, ABORT
	{
		fprintf(stderr, "ERROR: no trash environment variable set.\n");
		fprintf(stderr, "ERROR: set it with 'export TRASH=[trash directory]' in the shell.\n");
		usage();
	}
	
	// parse for the h flag
	while ((c = getopt(argc, argv, "h")) != EOF)
	{
		//indexToAdd = optind - 1;
		switch (c)
		{
			case '?':
			case 'h':
			default:
				errflag++;
		}
	}
	
	if (errflag) usage();
	else
	{
		if(globalDebug) printf("Starting to empty trash\n");
		// dump out the trash
		emptyTrash();
	}
	
	return 0;
}

/* Force delete skips the trash and deletes the given file.
 * If there is an error in the delete it returns -1,
 * otherwise it returns 0.
 */
int removeFile(char *file)
{
	int ret;
	ret = unlink(file);
	return ret;
}
 
/* Force directory delete does a recursive delete instead of a normal delete on the given file.
 * If there is an error in the delete it returns -1.
 */
int emptyTrash()
{
	if (globalDebug) printf("starting stuff\n");
	int ret, ret1;
	if (globalDebug) printf("declaring rets\n");
	DIR *trashPoint;
	if (globalDebug) printf("declared trashpoint\n");
	struct dirent *trashEntry;
	if (globalDebug) printf("declared trashentry\n");
	char *file;
	char theFile[MAX_SIZE];
	char *temp;
	char theTemp[(MAX_SIZE * 2) + 1];
	if (globalDebug) printf("declared chars\n");
	struct stat buf;
	
	temp = theTemp;
	file = theFile;
	
	if (globalDebug) printf("DEBUG: starting to empty the trash\n");
	trashEntry = readdir(trashPoint);
	file = trashEntry->d_name;
	if(globalDebug) printf("DEBUG: '%s' should be ./\n", file);
	trashEntry = readdir(trashPoint); // skip ahead two so it's not ./ or ../
	file = trashEntry->d_name;
	if(globalDebug) printf("DEBUG: '%s' should be ../\n", file);
	trashEntry = readdir(trashPoint);
	if (trashEntry)
	{
		file = trashEntry->d_name;
		if(globalDebug) printf("DEBUG: '%s' is the first file looked at\n", file);
	}
	else
	{
		if(globalDebug) printf("DEBUG: '%s' is now empty\n", trash);
	}
	while (trashEntry) // while the directory is non-empty, keep calling this func
	{
		file = trashEntry->d_name;
		if(globalDebug) printf("DEBUG: '%s' has a child, '%s'\n", trash, file);
		strncpy(temp, trash, stringLength(temp));
		strncat(temp, "/", stringLength(temp));
		strncat(temp, file, stringLength(trash) + 1 + stringLength(file));
		if(globalDebug) printf("DEBUG: This is what will be passed to newStat: '%s'\n", temp);
		newStat(temp, &buf);
		
		ret1 = dirDelete(temp, buf);
		
		if(globalDebug) printf("DEBUG: Back in %s\n", trash);
		trashEntry = readdir(trashPoint);
		if (trashEntry)
		{
			if(globalDebug) printf("DEBUG: found a new entry\n");
		}
		else
		{
			if(globalDebug) printf("DEBUG: no other entries found\n");
		}
	}
	closedir(trashPoint);
	
	if (ret1 < ret)
	{
		ret = ret1;
	}
	return ret;
}

/* dirOp() handles directories when it comes to removing */
int dirOp(char *dir)
{
	DIR *dp;
	struct dirent *entry;
	int fileSize = 257;
	int tempSize = (fileSize * 2) + 1;
	int ret, ret1;
	char *file;
	char theFile[fileSize];
	char *temp;
	char theTemp[tempSize];
	struct stat buf;
	
	file = theFile;
	temp = theTemp;
	
	if(globalDebug) printf("DEBUG: '%s' is a directory\n", dir);
	dp = opendir(dir);
	if (dp == NULL)
	{
		perror("open");
		exit(1);
	}
	entry = readdir(dp);
	file = entry->d_name;
	if(globalDebug) printf("DEBUG: '%s' should be ./\n", file);
	entry = readdir(dp); // skip ahead two so it's not ./ or ../
	file = entry->d_name;
	if(globalDebug) printf("DEBUG: '%s' should be ../\n", file);
	entry = readdir(dp);
	if (entry)
	{
		file = entry->d_name;
		if(globalDebug) printf("DEBUG: '%s' is the first file looked at\n", file);
	}
	else
	{
		if(globalDebug) printf("DEBUG: '%s' is now empty\n", dir);
	}
	while (entry) // while the directory is non-empty, keep calling this func
	{
		file = entry->d_name;
		if(globalDebug) printf("DEBUG: '%s' has a child, '%s'\n", dir, file);
		strncpy(temp, dir, stringLength(temp));
		strncat(temp, "/", stringLength(temp));
		strncat(temp, file, stringLength(dir) + 1 + stringLength(file));
		if(globalDebug) printf("DEBUG: This is what will be passed to newStat: '%s'\n", temp);
		newStat(temp, &buf);
		
		ret1 = dirDelete(temp, buf);
		
		if(globalDebug) printf("DEBUG: Back in %s\n", dir);
		entry = readdir(dp);
		if (entry)
		{
			if(globalDebug) printf("DEBUG: found a new entry\n");
		}
		else
		{
			if(globalDebug) printf("DEBUG: no other entries found\n");
		}
	}
	closedir(dp);
	if (ret1 < ret)
	{
		ret = ret1;
	}
	ret = rmdir(dir);
	if(globalDebug) printf("DEBUG: just deleted '%s'\n", dir);
	return ret;
}

/* Force directory delete does a recursive delete instead of a normal delete on the given file.
 * If there is an error in the delete it returns -1.
 */
int dirDelete(char *dir, struct stat fileBuffer)
{
	int ret;
	/* if it's a directory rm the directory */
	if (S_ISDIR(fileBuffer.st_mode))
	{
		ret = dirOp(dir);		
	}
	else // otherwise, it's a file, remove the file
	{
		if(globalDebug) printf("DEBUG: '%s' is a file\n", dir);
		ret = removeFile(dir);
	}
	return ret;
}

void usage(void)
{
	fprintf(stderr, "Usage: dump [OPTION]\n");
	fprintf(stderr, "OR: dump \n");
	fprintf(stderr, "Dump out the trash directory. This clears the trash directory.\n");
	fprintf(stderr, "\t-h\t\tdisplay this help message\n");
	exit(1);
}

/* NewStat() does a check to make sure stat() completed correctly */
void newStat(const char *file, struct stat *fileBuffer)
{
	int ret;
	/* check the stats of the file and put them in buf */
	ret = stat(file, fileBuffer);
	if (ret != 0)
	{
		perror("Failed in newStat:");
		exit(ret);
	}
}

int stringLength(char *string)
{
	int ret;
	ret = sizeof(string)/sizeof(char);
	return ret;
}

